import sys, os

from core import cmds
from core.base import NewComponentType, NewComponentInst, NewWidgetType, ComponentSelected, NewActionType, \
                      NewSignal, Signal, AttributeBase, NewAttributeType, ObjectSelected, \
                      NewNamedComponent
#from core.data import DataHandle
from core.manager import Scene
from core import util
from core.base import ComponentBase, DataHandle, Editor, Expression


from PyQt4.QtGui import *
from PyQt4.QtCore import *
from ui.models import SelectionModel
from core.manager import PluginManager


class ExpressionEditor(Editor):
    display = None
    widget = QLineEdit
    
    def setup(self):
        self.widget.connect(self.widget, SIGNAL("editingFinished()"), self.on_edit)
        print 'EXPRESSION', self.data.expression()
        self.widget.setText(self.data.expression())
        print str(self.widget.text())
        self.data.change.connect(self.on_data_change)
        
    
    def on_data_change(self, value):
        self.widget.setText(self.data.expression())
    
    def on_edit(self):
        print 'Expression Edited', self.widget.text()
        self.data.set(Expression(str(self.widget.text())))

class App(object):
    Current = None
    
    def __init__(self):
        self.current_component = None
        ComponentSelected.connect(self.set_current_component)
        
        
        self._signals = {}
        NewSignal.connect(self.register_signal)
        
        self.actions = {}
        NewActionType.connect(self.register_action_type)
        
        self.widgets = {}
        NewWidgetType.connect(self.register_widget_type)
        
        self.scene = Scene()
        
        self.qtapp = QApplication(sys.argv)
        self.main_window = MainWindow()
        
        self.set_as_current()
        
    
    def plugins(self):
        return PluginManager().plugins
        
    def select(self, obj):
        if isinstance(obj, ComponentBase):
            ComponentSelected(obj)
        else:
            ObjectSelected(obj)
        
    def get_signals(self):
        return Signal._signals
    
    signals = property(get_signals)
        
    def register_signal(self, signal):
        self._signals[signal.name] = signal
        
    def set_current_component(self, comp): 
        self.current_component = comp
        
    def set_db(self, db_path, override=False):
        if override and os.path.exists(db_path):
            with open(db_path, 'w'): pass
        self.scene.db.path = db_path
        self.scene.db.connect()

    def setup_listeners(self):
        NewComponentInst.connect(self.scene.add_instance)
        NewComponentType.connect(self.scene.add_type)
        NewNamedComponent.connect(self.scene.add_server_type)
        NewAttributeType.connect(self.scene.add_type)
        
    def reload_scene(self):
        self.scene.db.file_storage.close()
        self.scene = Scene()
        
    def register_action_type(self, cls):
        if not util.isiter(cls.types):
            cls.types = [cls.types]
        for atype in cls.types:
            if not self.actions.has_key(atype):
                self.actions[atype] = []
            self.actions[atype].append(cls)
        
    def register_widget_type(self, cls):
        ''' @todo: Register Editors here, maybe the App (instead of Scene) should just handle all new types
        '''
        if self.widgets.has_key(cls.attribute):
            self.widgets[cls.attribute] = cls
        else:
            self.widgets[cls.attribute] = cls
        
    def widget(self, attr):
        return self.widgets.get(attr)
    
    def editor(self, obj):
        attr = None
        if isinstance(obj, DataHandle):
            if obj.is_expression():
                return ExpressionEditor
            attr = obj.attr.__class__
        if isinstance(obj, AttributeBase):
            attr = obj.__class__
        if not attr: return
        widget = self.widget(attr)
        return widget
    
    def displayer(self, obj):
        attr = None
        if isinstance(obj, DataHandle):
            attr = obj.attr
        elif isinstance(obj, Attribute):
            attr = obj
        if not attr: return
        return self.widget(attr)
        
    def set_as_current(self):
        self.__class__.Current = self
        cmds.reload()

    def get_actions(self, obj):
        result_actions = []
        for atype, actions in self.actions.iteritems():
            for act in actions:
                if hasattr(act, 'has_action') and act.has_action(obj):
                    result_actions.append(act)
            
            if atype is None or not actions: continue
            if isinstance(obj, atype):
                result_actions.extend(actions)
        return list(set(result_actions))
    
    def create_action_menu(self, obj, widget):
        if widget is None:
            widget = self.main_window
        actions = self.get_actions(obj)
        if not actions: return None
        menu = QMenu(widget)
        for act_cls in actions:
            act = act_cls()
            if act.options:
                sub_menu = QMenu(menu)
                menu.addMenu(sub_menu)
                sub_menu.setTitle(str(act.get_name(obj)))
                for opt in act.options:
                    q_act = QAction(sub_menu)
                    q_act.setText(str(opt))
                    q_act.connect(q_act, SIGNAL("triggered()"), lambda act=act, obj=obj, option=opt: act.do(obj, option=option))
                    sub_menu.addAction(q_act)
            else:
                q_act = QAction(menu)
                q_act.setText(act.get_name(obj))
                q_act.connect(q_act, SIGNAL("triggered()"), lambda act=act, obj=obj: act.do(obj))
                menu.addAction(q_act)
        return menu
            

    def close(self):
        self.scene._db.file_storage.close()
    
    def update_ui(self):
        self.qtapp.processEvents()
    
    def run_ui(self):
        self.main_window.show()
        self.qtapp.exec_()



class PanelTitle(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        
        layout = QHBoxLayout()
        self.switch_view_btn = QToolButton(self)
        self.switch_view_btn.setPopupMode(QToolButton.InstantPopup)
        self.switch_view_act = QAction(self)
        self.switch_view_btn.setDefaultAction(self.switch_view_act)
        
        self.select_btn = QToolButton(self)
        self.select_act = QAction(self)
        self.select_act.setText("S")
        
        self.select_btn.setDefaultAction(self.select_act)
        
        self.views_menu = QMenu(self)
        self.switch_view_btn.setMenu(self.views_menu)
        
        layout.addWidget(self.switch_view_btn, stretch=0, alignment=Qt.AlignLeft)
        layout.addWidget(self.select_btn, stretch=0, alignment=Qt.AlignLeft)
        layout.setAlignment(Qt.AlignLeft)
        
        layout.setSpacing(0)
        layout.setMargin(0)
        
        self.setLayout(layout)
        
    def sizeHint(self):
        return self.layout().sizeHint()
    
    def minimumSizeHint(self):
        return self.layout().sizeHint()

class _Panel:
    def __init__(self, view):
        self.view = view
        self.titlebar = PanelTitle(self)
        self.connect(self.titlebar.views_menu, SIGNAL("aboutToShow()"), self.update_views_menu)
        self.connect(self.titlebar.select_act, SIGNAL("triggered()"), self.select_view)
        self.update_views_menu()
        
    def select_view(self):
        cmds.select(self.view)
        
    def set_view(self, view):
        print 'SETTING VIEW', view
        self.view = view
        widget = view.new_widget()
        self.setWidget(widget)
        self.update_views_menu()
        
    def widget(self):
        ''' Must be overriden, and return the widget that the panel holds '''
        
    def setWidget(self):
        ''' Must be overriden, set the widget for the panel'''
        
    def recreate_widget(self):
        widget = self.widget()
        widget.deleteLater()
        new_widget = self.view.new_widget()
        self.setWidget(new_widget)
        
    def update_views_menu(self):
        views_menu = self.titlebar.views_menu
        views_menu.clear()
        self.titlebar.switch_view_act.setText(self.view.name)
        self.setWindowTitle(self.view.name)
        views_menu.setTitle(self.view.name)
        for view in cmds.ls("View"):
            if view is self.view:
                continue
            act = QAction(self)
            act.setText(view.name)
            self.connect(act, SIGNAL("triggered()"), lambda view=view: self.set_view(view))
            views_menu.addAction(act)
        self.update()

class WidgetPanel(QWidget, _Panel):
    def __init__(self, view, parent):
        QWidget.__init__(self, parent)
        _Panel.__init__(self, view)
        layout = QVBoxLayout()
        layout.addWidget(self.titlebar)
        self._widget = None
        #layout.addWidget(widget)
        layout.setSpacing(0)
        layout.setMargin(0)
        self.setLayout(layout)
        
    def widget(self):
        return self._widget
        
    def setWidget(self, widget):
        widget.setParent(self)
        if self._widget is not None:
            self._widget.deleteLater()
        self.layout().addWidget(widget)
        self._widget = widget
        
        

class DockPanel(QDockWidget, _Panel):
    def __init__(self, view, parent):
        QDockWidget.__init__(self, parent)
        _Panel.__init__(self, view)
        self.setTitleBarWidget(self.titlebar)
    
    def widget(self):
        return QDockWidget.widget(self)
    
    def setWidget(self, widget):
        QDockWidget.setWidget(self, widget)
        
        

class MainWindow(QMainWindow):
    dock_map = {"left":     Qt.LeftDockWidgetArea,
                "top":      Qt.TopDockWidgetArea,
                "bottom":   Qt.BottomDockWidgetArea,
                "right":    Qt.RightDockWidgetArea }
    
    def __init__(self):
        QMainWindow.__init__(self, None)
        self.setWindowTitle("Open Pipeline")
        self.views = {}
        self.panels = []
        self.menubars = {}
        self._menubar = QMenuBar(self)
        self.setMenuBar(self._menubar)
        
        self.refresh_act = QAction(self)
        self.refresh_act.setText("Refresh Views")
        self.refresh_act.setShortcut(QKeySequence.fromString("F5"))
        self.connect(self.refresh_act, SIGNAL("triggered()"), self.update_views)
        
        self.recreate_act = QAction(self)
        self.recreate_act.setText("Recreate Widgets")
        self.recreate_act.setShortcut(QKeySequence.fromString("F6"))
        self.connect(self.recreate_act, SIGNAL("triggered()"), self.recreate_widgets)
        
        self.addAction(self.refresh_act)
        self.addAction(self.recreate_act)        
        #self.set
        #self.setDockOptions(QMainWindow.ForceTabbedDocks)
        
    def recreate_widgets(self):
        print 'recreating widgets'
        for panel in self.panels:
            print 'recreating view', panel
            panel.recreate_widget()
        
    def update_views(self):
        for view, widgets in self.views.items():
            view.update_widgets()
        
    def has_view(self, view):
        return self.views.has_key(view)
        
    def add_menu(self, menu):
        widget = menu.new_widget()
        if not isinstance(widget, QMenu): return
        menu_bar = self._menubar.addMenu(widget)
        self.menubars[menu] = widget
        
    def closeEvent(self, event):
        print 'Closing Main Window'
        event.accept()
        
    def views_at(self, position):
        dock_pos = self.dock_map.get(position)
        if not dock_pos: raise StopIteration()
        for view, docks in self.views.items():
            for dock in docks:
                if not isinstance(dock, QDockWidget): continue
                if self.dockWidgetArea(dock) == self.dock_map[position]:
                    yield (view, dock)
        
    def add_view(self, view, position="left", tab=True):
        ''' Add a View to this main window.
            The view widget will be added as either a Dock widget or to the center.
        '''

        #widget = view._init_widget()
        widget = view.new_widget()
        if isinstance(widget, QWidget):
            panel = self._add_widget(view, widget, position)
            self.panels.append(panel)
            return
            
            if not self.views.has_key(view):
                self.views[view] = []
            
            if tab:
                view_docks = list(self.views_at(position))
                if view_docks:
                    self.tabifyDockWidget(view_docks[0][1], parent_widget)
                    
            self.views[view].append(parent_widget)
                        
    def rem_view(self, view):
        ''' Remove the view from the main window '''
        
        widgets = self.views.get(view)
        if not widgets:
            raise ValueError("View is not in Main Window")
        for widget in widgets:
            if isinstance(widget, QDockWidget):
                self.removeDockWidget(widget)
        del self.views[view]
            
            
    def _add_widget(self, view, widget, position):
        if position.lower() == "center":
            panel = WidgetPanel(view, self)
            panel.setWidget(widget)
            self.setCentralWidget(panel)
            return panel
        
        if position.lower() == "window":
            widget.setParent(None)
            widget.show()
            return 
        
        dock_area = self.dock_map.get(position.lower())
        dock_widget = DockPanel(view, self)
        dock_widget.setWidget(widget)
        dock_widget.setWindowTitle(str(view))
        self.addDockWidget(dock_area, dock_widget)
        return dock_widget
            


